import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.Scanner;
public class Provider implements Runnable{
    ServerSocket providerSocket;
    Socket connection = null;
    Socket connection2 = null;
    Socket connection3 = null;
    ObjectOutputStream mostRecentOut;
    ObjectOutputStream out;
    ObjectInputStream in;
    ObjectOutputStream out2;
    ObjectInputStream in2;
    ObjectOutputStream out3;
    ObjectInputStream in3;
    String message;
    Provider(){}
    @SuppressWarnings("unchecked")
    public void run()
    {
        try{
            //1. creating a server socket
            providerSocket = new ServerSocket(2413, 10);
            //2. Wait for connection
            System.out.println("Waiting for connection");
            connection = providerSocket.accept();
            System.out.println("Connection received from " + connection.getInetAddress().getHostName());
            System.out.println("Waiting for connection 2");
            connection2 = providerSocket.accept();
            System.out.println("Connection received from " + connection2.getInetAddress().getHostName());
            System.out.println("Waiting for connection 3");
            connection3 = providerSocket.accept();
            System.out.println("Connection received from " + connection3.getInetAddress().getHostName());
            //3. get Input and Output streams
            out = new ObjectOutputStream(connection.getOutputStream());
            out.flush();
            out2 = new ObjectOutputStream(connection2.getOutputStream());
            out2.flush();
            out3 = new ObjectOutputStream(connection3.getOutputStream());
            out3.flush();
            in = new ObjectInputStream(connection.getInputStream());
            in2 = new ObjectInputStream(connection2.getInputStream());
            in3 = new ObjectInputStream(connection3.getInputStream());
            //4. The two parts communicate via the input and output streams
            MessageThread m1 = new MessageThread(out, in, this);
            MessageThread m2 = new MessageThread(out2, in2, this);
            MessageThread m3 = new MessageThread(out3, in3, this);
            //start the message threads these will be used to relay commands to clients
            Thread message_thread1 = (new Thread(m1));
            message_thread1.start();
            Thread message_thread2 = (new Thread(m2));
            message_thread2.start();
            Thread message_thread3 = (new Thread(m3));
            message_thread3.start();
            do{
                try{
                    //do nothing the server runs forever
                }
                catch (NullPointerException e) {
                    //System.out.println("Null recieved");
                    //
                }
            }while(true);
        }
        catch(IOException ioException){
            ioException.printStackTrace();
        }
        finally{
            //4: Closing connection
            try{          
                ArrayList<String> messages = null;
            do{
                try{
                    Object messages_obj = in.readObject();
                    if(messages_obj instanceof String){

                        //client sent grep request relay to all the clients
                        System.out.println("message recieved : "+(String)messages_obj);
                    }
                    else if(messages_obj.getClass().equals(ArrayList.class)){
                        messages = (ArrayList<String>) messages_obj;
                        if(messages != null){
                            for(int i =0; i< messages.size(); i++)
                                System.out.println(messages.get(i));
                        }
                    }
                }
                catch (NullPointerException e) {
                    System.out.println("Null recieved");
                    //
                }
                catch (ClassNotFoundException e) {
                    System.err.println("Data received in unknown format");
                }
            }while(true);
        }
        catch(IOException ioException){
            ioException.printStackTrace();
        }
        finally{
            //4: Closing connection
            try{
                in.close();
                out.close();
                providerSocket.close();
            }
            catch(IOException ioException){
                ioException.printStackTrace();
            }
        }
        }
    }
   
    //send a message to all clients
    void sendMessage(Object msg, ObjectOutputStream out)
    {
        try{
            out.writeObject(msg);
            out.flush();
        }
        catch(IOException ioException){
            ioException.printStackTrace();
        }
    }
   
    void sendMessageAll(Object msg)
    {
        try{
            out.writeObject(msg);
            out.flush();
            out2.writeObject(msg);
            out2.flush();
            out3.writeObject(msg);
            out3.flush();
        }
        catch(IOException ioException){
            ioException.printStackTrace();
        }
    }
   
    public static void main(String args[])
    {
        Provider server = new Provider();
        Thread server_thread = (new Thread(server));
        server_thread.start();

    }
   
    public class MessageThread implements Runnable{
        ObjectOutputStream out;
        ObjectInputStream in;
        Provider server;
       
        public MessageThread(ObjectOutputStream out, ObjectInputStream in, Provider p){
            this.in = in;
            this.out = out;
            this.server = p;
        }
       
        @SuppressWarnings("unchecked")
        @Override
        public void run() {
            ArrayList<String> messages = null;
            do{
                try{
                    Object messages_obj = null;
                    try {
                        messages_obj = in.readObject();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    if(messages_obj instanceof String){
                        mostRecentOut = this.out;
                        //client sent grep request relay to all the clients
                        System.out.println("message recieved : "+(String)messages_obj);
                        server.sendMessageAll((String)messages_obj);
                    }
                    else if(messages_obj.getClass().equals(ArrayList.class)){
                        messages = (ArrayList<String>) messages_obj;
                        if(messages != null){
                            for(int i =0; i< messages.size(); i++)
                                System.out.println(messages.get(i));
                            server.sendMessage(messages, mostRecentOut);
                        }
                    }
                }
                catch (NullPointerException e) {
                    System.out.println("Null recieved");
                    //
                }
                catch (ClassNotFoundException e) {
                    System.err.println("Data received in unknown format");
                }
            }while(true);
        }
    }
}